<template>
	<!-- action: 立即上传 http-request -->
	<el-upload
		class="avatar-uploader"
		action="#"
		:http-request="uploadRequest"
		:limit="300"
		:before-upload="beforeUpload"
		:on-success="handleSuccess"
		:on-exceed="handleExceed"
		:on-change="handleChange"
	>
		<template #trigger>
			<el-button type="primary">select file</el-button>
		</template>
		<!-- <el-button class="ml-3" type="success" @click="submitHandler"> upload to server </el-button> -->
	</el-upload>
	<el-progress :stroke-width="6" :percentage="progressPercent"></el-progress>
	<el-button @click="pauseUpload" type="warning" :disabled="!isUploading">暂停上传</el-button>
	<el-button @click="cancelUpload" type="danger" :disabled="!isUploading">取消上传</el-button>
	<el-button @click="resumeUpload" type="success" :disabled="!isPaused">继续上传</el-button>
	<!-- <el-progress v-show="showProgress" :stroke-width="6" :percentage="progressPercent"></el-progress> -->
</template>

<script setup lang="ts">
import { instance } from '@/utils'
import { CancelTokenSource } from 'axios'

import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import type { UploadFile } from 'element-plus'
import SparkMD5 from 'spark-md5'
const chunkSize = 1024 * 1024 * 5
let cancelTokenSource: CancelTokenSource | null = null
let currentChunk = ref(0) // 当前上传的分片索引
let totalChunks = ref(0) // 总分片数

const imageUrl = ref('')
const form = reactive({
	file: null,
	filename: null
})

// 进度条
const showProgress = ref(false)
const progressPercent = ref(0) // 进度条百分比

/**
 * 文件超出个数限制时的钩子
 */
const handleExceed = () => {
	ElMessage.error('超出最大上传限制')
}
const fileListData = ref([])
// 覆盖默认行为,正常情况是在这一层做 数据请求，
const uploadRequest = (params) => {
	console.log('%c [params  ]-51', 'font-size:13px; background:pink; color:#bf2c9f;', params)
	imageUrl.value = URL.createObjectURL(params.file)
	form.file = params.file
	form.filename = params.file.name
}
/**
 * 谁对资源进行分快，资源整合 《怎么知道前端已经上传完成》
 * 失败怎么处理
 * 切片上传
 * 断点续传
 * 秒传
 选择上传资源，判断文件大小，大于范围，进行切片，再分片续传
 检查上传文件，断点续传
 前端上传完成后，发送一个merge 请求
 检查 分片。 分片上传  合并分片
 */
const submitHandler = async () => {}
const changeBuffer = (file) => {
	return new Promise((resolve, reject) => {
		let fileReader = new FileReader()
		fileReader.readAsArrayBuffer(file)
		fileReader.onload = (e) => {
			let buffer = e.target.result,
				spark = new SparkMD5.ArrayBuffer(),
				HASH = spark.append(buffer).end(),
				suffix = file.name.split('.').pop()
			resolve({
				HASH,
				suffix,
				filename: `${HASH}.${suffix}`
			})
		}
	})
}
const getChunkSize = (size) => {
	//  // 5MB default
	let chunkSize = 1024 * 1024 * 5 // 5MB default
	let chunkCount = Math.ceil(size / chunkSize)
	if (chunkCount > 100) {
		chunkSize = size / 100
		chunkCount = 100
	}
	return { chunkSize, chunkCount }
}

/**
 * 将文件分片并封装成对象
 * @param {File} file - 要分片的文件
 * @param {number} chunkSize - 每个分片的大小
 * @param {string} HASH - 分片文件名的前缀
 * @param {string} suffix - 文件后缀
 * @returns {Array} 分片对象数组
 */
function createFileChunks(file, chunkSize, HASH, suffix) {
	// 计算总分片数
	const chunkCount = Math.ceil(file.size / chunkSize)

	// 定义分片数组
	const chunks = []
	// 检查上传索引
	let index = 0

	/**
	 * 分片上传
	 */
	while (index < chunkCount) {
		// 生成分片
		const blob = file.slice(index * chunkSize, (index + 1) * chunkSize)

		// 生成分片对象
		const chunk = {
			file: blob,
			filename: `${HASH}_${index + 1}.${suffix}`
		}

		// 添加到分片数组
		chunks.push(chunk)

		// 更新分片索引
		index++
	}
	totalChunks.value = chunks.length

	return chunks
}

/**
 *
 * @param file
 * @param fileList
   文件状态改变时的钩子
	 秒传逻辑：
	 1. 获取已经上传的文件
	 2. 获取文件的HASH
	 3. 传递给upload_already 接口，如果已经传递过，则直接提示用户，已经传递完成
 */
const handleChange = async (file: UploadFile, fileList: UploadFile[]) => {
	// console.log('%c [  ]-149', 'font-size:13px; background:pink; color:#bf2c9f;', file.status)
	// const list = await getFileMd5(file.raw, 10)

	// console.log('%c [ ;ist ]-185', 'font-size:13px; background:pink; color:#bf2c9f;', list)
	// return
	// const handleChange = async (file, fileList) => {
	console.log('%c [ fileList ]-85', 'font-size:13px; background:pink; color:#bf2c9f;', fileList)
	console.log('%c [  ]-85', 'font-size:13px; background:pink; color:#bf2c9f;', file)
	// if (file.status === 'ready') {
	// 	progressPercent.value = 0
	// 	showProgress.value = true
	// 	const interval = setInterval(() => {
	// 		if (progressPercent.value >= 99) {
	// 			clearInterval(interval)
	// 			return
	// 		}
	// 		progressPercent.value += 1
	// 	}, 50)
	// }
	// if (file.status === 'success') {
	// 	progressPercent.value = 100
	// 	showProgress.value = false // 隐藏进度条
	// }

	const uploadFile = file.raw

	// 获取已经上传的文件
	// 获取文件的HASH
	let already = []
	let data = null
	let { HASH, suffix } = await changeBuffer(uploadFile)
	try {
		// 要做秒传，这里应该还有一个标识，来告诉客户端，
		const data = await instance.get('/upload_already', {
			params: {
				HASH
			}
		})
		if (+data.code === 0) {
			already = data.fileList
		}
	} catch (error) {}
	/**
	 * 开始进行分片处理，开始传递
			1. 定义分片的大小：
	 		2. 计算分片的数量 （不能太多），控制粒度，如果太多，要发跟多请求，如果分的太少，又可能会降低上传速度。
			  2.1 ，如果chunkCount > 100, 改变 chunkSize = file.size / 100,
	 */
	// // 定义分片大小 固定数量 固定大小？  不过这里还是拿原始数据做计算，不会影响
	const { chunkSize, chunkCount } = getChunkSize(file.size)
	let chunks = createFileChunks(uploadFile, chunkSize, HASH, suffix)
	let index = 0
	// chunks.forEach(async (chunk) => {
	// 	// 已经上传。无需再传 秒传和续传 都实现了， 很好
	// 	if (already.length && already.includes(chunk.filename)) {
	// 		complate()
	// 		return
	// 	}
	// 	// 如果全部传完，则告诉客户端，全部传递完毕 return 后面的行为
	// 	// 开始上传
	// 	let fm = new FormData()
	// 	fm.append('file', chunk.file)
	// 	fm.append('filename', chunk.filename)
	// 	const data = await instance.post('/upload_chunk', fm)

	// 	if (+data.code === 0) {
	// 		complate()
	// 		return
	// 	}
	// })
	const complete = async () => {
		console.log('%c [ index ]-202', 'font-size:13px; background:pink; color:#bf2c9f;', index)
		index++
		if (index < chunkCount) return // 只有全部上传完毕，才merge
		// 合并分片·
		// mergeChunk(chunks)
		try {
			data = await instance.post(
				'/upload_merge',
				{
					HASH,
					chunkCount
				},
				{
					headers: {
						'Content-Type': 'application/x-www-form-urlencoded'
					}
				}
			)
			// 等到所有文件都执行完毕，也就是index 和 count 相等的时候 合并文件
			if (+data.code === 0) {
				ElMessage.success('文件上传完毕')
			}
		} catch (error) {
			ElMessage.error('文件上传失败，请稍后重试')
		}
	}
	// 上传分片
	const uploadChunk = async (chunk) => {
		// 已经上传。无需再传 秒传和续传 都实现了， 很好

		if (already.includes(chunk.filename)) {
			console.log('%c [ already！！！！ ]-285', 'font-size:13px; background:pink; color:#bf2c9f;', already)
			complete()
			return
		}
		// 如果全部传完，则告诉客户端，全部传递完毕 return 后面的行为
		// 开始上传
		const formData = new FormData()
		formData.append('file', chunk.file)
		formData.append('filename', chunk.filename)

		try {
			const response = await instance.post('/upload_chunk', formData, {
				cancelToken: cancelTokenSource?.token
			})
			currentChunk.value++
			progressPercent.value = Math.floor((currentChunk.value / totalChunks.value) * 100)

			console.log('%c [ response ]-295', 'font-size:13px; background:pink; color:#bf2c9f;', response)

			if (+response.code === 0) {
				complete()
			}
		} catch (error) {
			console.error('Error uploading chunk:', error)
		}
	}

	// 上传所有分片
	chunks.forEach(uploadChunk)
	progressPercent.value = chunks.length
}
const pauseUpload = () => {
	if (cancelTokenSource) {
		cancelTokenSource.cancel('上传已暂停')
		isPaused.value = true
		isUploading.value = false
	}
}

const cancelUpload = () => {
	if (cancelTokenSource) {
		cancelTokenSource.cancel('上传已取消')
		resetUploadState()
	}
}

const resumeUpload = () => {
	if (isPaused.value) {
		startUpload()
	}
}
</script>

<style scoped lang="scss">
.upload-wrap {
	border: 1px solid blue;
	width: 50vh;
	height: 100%;

	padding: 10px;
}

.avatar-uploader .avatar {
	width: 178px;
	height: 178px;
	display: block;
}
</style>

<style>
.avatar-uploader .el-upload {
	border: 1px dashed var(--el-border-color);
	border-radius: 6px;
	cursor: pointer;
	position: relative;
	overflow: hidden;
	transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
	border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
	font-size: 28px;
	color: #8c939d;
	width: 178px;
	height: 178px;
	text-align: center;
}
</style>
